<?php

/*
 * Camelot PHP Tools
 *
 * @version 2.0
 * @author Bendsoft
 * @package PHP Tools
 * @subpackage Camelot
 * @license FreeBSD License (www.bendsoft.com/licensing/)
 * 
 */

/**
 * Base functionality all Procedure Commands
 * @author Bendsoft
 */
class ProcedureCommandBase {

    /**
     * Name of the connection
     * @var string
     */
    public $connection_name;

}

/**
 * Command to create folders
 * @author Bendsoft
 * @link http://www.bendsoft.com/documentation/camelot-net-connector/latest/procedures/createfolder/ Official documentation
 */
class CreateFolderCommand extends ProcedureCommandBase {

    /**
     * The name of the Document Library
     * @var string
     */
    public $listName;

    /**
     * The name of the folder, to enable recursive creation set the recursive property to true
     * @var string
     */
    public $folder;

    /**
     * Optional. 
     * Enable recursive creation of folders or not
     * @var bool
     */
    public $recursive = false;

    /**
     * Construct a CreateFolderCommand
     * @param string $connection_name
     * @param string $listName The name of the Document Library
     * @param string $folder The name of the folder, to enable recursive creation set the recursive property to true
     * @param bool $recursive (optional) Enable recursive creation of folders or not
     */
    public function __construct($connection_name = null, $listName = null, $folder = null, $recursive = false) {
        $this->connection_name = $connection_name;
        $this->listName = $listName;
        $this->folder = $folder;
        $this->recursive = $recursive;
    }

    /**
     * Check validity of the command
     * @param CreateFolderCommand $command
     * @return \CamelotException|boolean
     */
    public function isValid(CreateFolderCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the connection name
        if (empty($command->connection_name)) {
            return new CamelotException(null, 606);
        }

        // Ojbect specific validations
        if (empty($command->listName)) {
            return new CamelotException(null, 605);
        }
        if (empty($command->folder)) {
            return new CamelotException(null, 607);
        }

        // If all is OK
        return true;
    }

    /**
     * Execute the command
     * @param CreateFolderCommand $command
     * @return \CamelotException|boolean
     */
    public function execute(CreateFolderCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Set method call as recursive or not
        $recursive = ($command->recursive) ? "true" : "false";

        // Build the command
        $ProcedureCommand = "CALL CREATEFOLDER('$command->listName', '$command->folder', $recursive)";

        try {
            // Build the command object
            $commandObject = new SharePointNonQueryCommand($command->connection_name, $ProcedureCommand);

            // Execute the command
            $SharePointNonQuery = new SharePointNonQuery($commandObject);

            if (isset($SharePointNonQuery->_result) && $SharePointNonQuery->_result == 1) {
                return true;
            } else {
                return false;
            }
        } catch (SoapFault $exc) {
            return new CamelotException($exc);
        }
    }

}

/**
 * Command to create document sets
 * @author Bendsoft
 * @since 2.0
 * @link http://www.bendsoft.com/documentation/camelot-net-connector/latest/procedures/createdocumentset/ Official documentation
 */
class CreateDocumentSetCommand extends ProcedureCommandBase {

    /**
     * The name of the Document Library
     * @var string
     */
    public $listName;

    /**
     * The name of the document set, to enable recursive creation set the recursive property to true
     * @var string
     */
    public $folder;

    /**
     * Name of the content type
     * @var string
     */
    public $contentType;

    /**
     * Optional
     * Enable recursive creation of folders or not
     * @var bool
     */
    public $recursive = false;

    /**
     * Construct a CreateFolderCommand
     * @param string $connection_name
     * @param string $listName The name of the Document Library
     * @param string $folder The name of the document set, to enable recursive creation set the recursive property to true
     * @param type $contentType Name of the content type
     * @param bool $recursive (optional) Enable recursive creation of folders or not
     */
    public function __construct($connection_name = null, $listName = null, $folder = null, $contentType = null, $recursive = false) {
        $this->connection_name = $connection_name;
        $this->listName = $listName;
        $this->folder = $folder;
        $this->contentType = $contentType;
        $this->recursive = $recursive;
    }

    /**
     * Check validity of the command
     * @param CreateDocumentSetCommand $command
     * @return \CamelotException|boolean
     */
    public function isValid(CreateDocumentSetCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the connection name
        if (empty($command->connection_name)) {
            return new CamelotException(null, 606);
        }

        // Ojbect specific validations
        if (empty($command->listName)) {
            return new CamelotException(null, 605);
        }
        if (empty($command->folder)) {
            return new CamelotException(null, 607);
        }
        if (empty($command->contentType)) {
            return new CamelotException(null, 608);
        }

        // If all is OK
        return true;
    }

    /**
     * Execute the command
     * @param CreateDocumentSetCommand $command
     * @return \CamelotException|boolean
     */
    public function execute(CreateDocumentSetCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Set method call as recursive or not
        $recursive = ($command->recursive) ? "true" : "false";

        // Build the command
        $ProcedureCommand = "CALL CREATEDOCUMENTSET('$command->listName', '$command->folder', '$command->contentType', $recursive)";

        try {
            // Build the command object
            $commandObject = new SharePointNonQueryCommand($command->connection_name, $ProcedureCommand);

            // Execute the command
            $SharePointNonQuery = new SharePointNonQuery($commandObject);

            if ($SharePointNonQuery->_result == 1) {
                return true;
            } else {
                return false;
            }
        } catch (SoapFault $exc) {
            return new CamelotException($exc);
        }
    }

}

/**
 * Command to create list instances from templates
 * @author Bendsoft
 * @since 2.1
 * @link http://www.bendsoft.com/documentation/camelot-net-connector/latest/procedures/createlistfromtemplate/ Official documentation
 */
class CreateListFromTemplateCommand extends ProcedureCommandBase {

    /**
     * The name of the new list instance
     * @var string
     */
    public $listName;

    /**
     * The description of the new list instance
     * @var string
     */
    public $description;

    /**
     * Name of the template to use
     * @var string
     */
    public $templateName;

    /**
     * Optional
     * Enable recursive creation of folders or not
     * http://egilhansen.com/2012/10/05/remember-featureid-when-creating-custom-listinstance-sharepoint-2010
     * @var guid
     */
    public $featureId = null;

    /**
     * Construct a CreateFolderCommand
     * @param string $connection_name
     * @param string $listName The name of the Document Library
     * @param string $folder The name of the document set, to enable recursive creation set the recursive property to true
     * @param type $contentType Name of the content type
     * @param bool $recursive (optional) Enable recursive creation of folders or not
     */
    public function __construct($connection_name = null, $listName = null, $folder = null, $contentType = null, $recursive = false) {
        $this->connection_name = $connection_name;
        $this->listName = $listName;
        $this->folder = $folder;
        $this->contentType = $contentType;
        $this->recursive = $recursive;
    }

    /**
     * Check validity of the command
     * @param CreateDocumentSetCommand $command
     * @return \CamelotException|boolean
     */
    public function isValid(CreateDocumentSetCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the connection name
        if (empty($command->connection_name)) {
            return new CamelotException(null, 606);
        }

        // Ojbect specific validations
        if (empty($command->listName)) {
            return new CamelotException(null, 605);
        }
        if (empty($command->folder)) {
            return new CamelotException(null, 607);
        }
        if (empty($command->contentType)) {
            return new CamelotException(null, 608);
        }

        // If all is OK
        return true;
    }

    /**
     * Execute the command
     * @param CreateDocumentSetCommand $command
     * @return \CamelotException|boolean
     */
    public function execute(CreateDocumentSetCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Set method call as recursive or not
        $recursive = ($command->recursive) ? "true" : "false";

        // Build the command
        $ProcedureCommand = "CALL CREATEDOCUMENTSET('$command->listName', '$command->folder', '$command->contentType', $recursive)";

        try {
            // Build the command object
            $commandObject = new SharePointNonQueryCommand($command->connection_name, $ProcedureCommand);

            // Execute the command
            $SharePointNonQuery = new SharePointNonQuery($commandObject);

            if ($SharePointNonQuery->_result == 1) {
                return true;
            } else {
                return false;
            }
        } catch (SoapFault $exc) {
            return new CamelotException($exc);
        }
    }

}

/**
 * Command to upload files. Upload commands will use a specialized method in
 * the Camelot SharePoint Integration Service for better performance and will
 * not invoke the normal CALL command.
 * @author Bendsoft
 * @link http://www.bendsoft.com/documentation/camelot-net-connector/latest/procedures/upload/ Official documentation
 */
class UploadCommand extends ProcedureCommandBase {

    /**
     * The name of the Document Library
     * @var string
     */
    public $listName;

    /**
     * Optional.
     * The name of the file and path in SharePoint (FolderName/helloworld.txt)
     * @var string
     */
    public $file;

    /**
     * The post data, the byte array or the $_FILES object
     * @var bytearray|array
     */
    public $data;

    /**
     * Optional.
     * Folderpath (My folder/Another folder)
     * @var string
     */
    public $folder;

    /**
     * Optional.
     * Path to file if uploading directly from disk (c:\temp\file.docx)
     * @var string
     */
    public $filePath;

    /**
     * Private store for the bytearray
     * @var bytearray
     */
    private $byteArray;

    /**
     * Construct an UploadCommand
     * @param string $connection_name
     * @param string $listName The name of the Document Library
     * @param string $file (optional) The name of the file and path in SharePoint (FolderName/helloworld.txt)
     * @param bytearray|array $data The post data, the byte array or the $_FILES object
     * @param string $folder (optional) Folderpath (My folder/Another folder)
     * @param string $filePath (optional) Path to file if uploading directly from disk (c:\temp\file.docx)
     */
    public function __construct($connection_name = null, $listName = null, $file = null, $data = null, $folder = null, $filePath = null) {
        $this->connection_name = $connection_name;
        $this->listName = $listName;
        $this->file = $file;
        $this->data = $data;
        $this->folder = $folder;
        $this->filePath = $filePath;
    }

    /**
     * Check validity of the command
     * @param UploadCommand $command
     * @return \CamelotException|boolean
     */
    public function isValid(UploadCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the connection name
        if (empty($command->connection_name)) {
            return new CamelotException(null, 606);
        }

        // Ojbect specific validations
        if (empty($command->listName)) {
            return new CamelotException(null, 605);
        }
        //if(empty($command->data)) {
        //    return new CamelotException(null, 600);
        //}
        //if(empty($command->file)) {
        //    return new CamelotException(null, 609);
        //}
        // If all is OK
        return true;
    }

    /**
     * Execute the command
     * @param UploadCommand $command
     * @return \CamelotException|boolean
     */
    public function execute(UploadCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the file
        $validateFileResult = $this->validate_file();
        if ($validateFileResult !== true && get_class($validateFileResult) == "CamelotException") {
            // Return the exception if it fails
            return $validateFileResult;
        }

        $uploadFileResult = $this->upload_file();
        return $uploadFileResult;
    }

    /**
     * Method to validate the file
     * @return \CamelotException|boolean
     */
    private function validate_file() {

        $file = "";
        $fileName = "";
        $size = 0;

        // If we are handing a $_FILES object
        if (gettype($this->data) == "array" && isset($this->data['file']) && $this->data['file']['error'] != 4) {

            $file = $this->data["file"];
            $fileName = $file["name"];
            $size = $file["size"];

            // Set the filename
            $this->file = empty($this->file) ? $fileName : $this->file;

            // Check for errors
            if ($file["error"] > 0) {
                return new CamelotException(null, 604);
            }

            // Set the byte array
            $this->byteArray = file_get_contents($file['tmp_name']);
        } elseif (!empty($this->filePath)) {
            $fileName = pathinfo($this->filePath, PATHINFO_BASENAME); //["basename"];
            $size = filesize($this->filePath);

            // Set the filename
            $this->file = empty($this->file) ? $fileName : $this->file;

            // Set the byte array
            $this->byteArray = file_get_contents($this->filePath);
        } elseif (isset($this->data) && gettype($this->data) != "array") {
            // If we are here a bytearray should have been loaded in the data property
            // Get the size
            $size = strlen($this->data);

            // if the filename isn't set return error
            if (empty($this->file)) {
                return new CamelotException(null, 619);
            }

            // Set the filename
            $fileName = empty($this->file) ? $fileName : $this->file;

            // Set the byte array
            $this->byteArray = $data;
        } else {
            // If nothing is set
            return new CamelotException(null, 600);
        }


        // Check valid file types
        if (constant("FILE_TYPES") != "*") {
            // Get file extension
            $ext = pathinfo($fileName, PATHINFO_EXTENSION);

            // Turn valid file types into an array
            $valid_extensions = explode(",", constant("FILE_TYPES"));

            // If not found...
            if (!in_array(strtolower($ext), array_map('strtolower', $valid_extensions))) {
                return new CamelotException(null, 602);
            }
        }

        // File size checks
        // ...check the file is larger than zero
        if (!$size > 0) {
            return new CamelotException(null, 603);
        }

        // ...check max size
        if (constant("FILE_SIZE") != "*") {
            // If bigger than the allowed maximum
            if ($size > (int) constant("FILE_SIZE")) {
                return new CamelotException(null, 601);
            }
        }

        // Add more validation methods if required...
        return true;
    }

    /**
     * Perform the file upload
     * @return \CamelotException|boolean
     */
    private function upload_file() {

        // Get the connection settings from the WcfSettingsList
        $WcfSettingsList = WcfSettingsList::instance();
        $settings = $WcfSettingsList->getSettings($this->connection_name);

        if (is_null($settings)) {
            return new CamelotException(null, 800);
        }
        try {
            // Set up connection to WSDL Client
            $client = new CamelotSoapClient($settings);

            $folder = (isset($this->folder)) ? rtrim($this->folder, '/') . '/' : "";

            $args = array(
                'listName' => $this->listName,
                'file' => $folder . $this->file,
                'data' => $this->byteArray,
                'connectionString' => $settings->connection_string,
                'sharedKey' => $settings->wsdl_shared_key
            );

            // Send file to WCF service and store result
            $uploadFile = $client->client->UploadFile($args)->UploadFileResult;

            if ($uploadFile == 1) {
                // If success
                return true;
            } else {
                // If fail
                return false;
            }
        } catch (SoapFault $exc) {
            // if we had a script error
            return new CamelotException($exc);
        }
    }

    /**
     * Destroy
     */
    public function __destruct() {
        unset($this->byteArray);
        unset($this->data);
    }

}

/**
 * Command to download files. Download commands will use a specialized method in
 * the Camelot SharePoint Integration Service for better performance and will
 * not invoke the normal CALL command.
 * @author Bendsoft
 * @link http://www.bendsoft.com/documentation/camelot-net-connector/latest/procedures/download/ Official documentation
 */
class DownloadCommand extends ProcedureCommandBase {

    /**
     * The name of the Document Library
     * @var string
     */
    public $listName;

    /**
     * The name of the file and path (FolderName/helloworld.txt)
     * @var string
     */
    public $file;

    /**
     * Optional
     * The version number (in example 2.4)
     * @var double
     */
    public $version;

    /**
     * Optional.
     * Path where to store the file on server, if not set the file will be sent 
     * to the client (/Users/Bendsoft/Web/Temp/)
     * @var string 
     */
    public $storePath;

    /**
     * Construct a DownloadCommand
     * @param string $connection_name
     * @param string $listName The name of the Document Library
     * @param string $file The name of the file and path (FolderName/helloworld.txt)
     * @param double $version The version number (in example 2.4)
     * @param string $storePath (optional) Path where to store the file on server, if not set the file will be sent to the client 
     */
    public function __construct($connection_name = null, $listName = null, $file = null, $version = null, $storePath = null) {
        $this->connection_name = $connection_name;
        $this->listName = $listName;
        $this->file = $file;
        $this->version = $version;
        $this->storePath = $storePath;
    }

    /**
     * Check validity of the command
     * @param DownloadCommand $command
     * @return \CamelotException|boolean
     */
    public function isValid(DownloadCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the connection name
        if (empty($command->connection_name)) {
            return new CamelotException(null, 606);
        }

        // Ojbect specific validations
        if (empty($command->listName)) {
            return new CamelotException(null, 605);
        }
        if (empty($command->file)) {
            return new CamelotException(null, 609);
        }

        // If all is OK
        return true;
    }

    /**
     * Execute the command
     * @param DownloadCommand $command
     * @return \CamelotException|boolean
     */
    public function execute(DownloadCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Get the connection settings from the WcfSettingsList
        $WcfSettingsList = WcfSettingsList::instance();
        $settings = $WcfSettingsList->getSettings($this->connection_name);

        if (is_null($settings)) {
            return new CamelotException(null, 800);
        }

        try {
            // Set up connection to WSDL Client
            $client = new CamelotSoapClient($settings);

            $args = array(
                'listName' => $this->listName,
                'file' => $this->file,
                'version' => empty($this->version) ? null : $this->version,
                'connectionString' => $settings->connection_string,
                'sharedKey' => $settings->wsdl_shared_key,
                'compression' => $settings->compression
            );

            // Get downloaded file from WCF Service
            $downloadedFile = $client->client->DownloadFile($args)->DownloadFileResult;

            // Send file to client
            if (empty($this->storePath)) {
                $this->download_file_to_client(pathinfo(stripslashes($this->file), PATHINFO_BASENAME), $downloadedFile); // ["basename"]
            }

            // Store on server
            if (!empty($this->storePath)) {
                // ...ensure the path ends with a /
                $storePath = rtrim($this->storePath, '/') . '/';

                // ...write file
                $fp = fopen($storePath . pathinfo(stripslashes($this->file), PATHINFO_BASENAME), 'wb'); // ["basename"]
                fwrite($fp, $downloadedFile);
                fclose($fp);
                return true;
            }
        } catch (SoapFault $exc) {
            return new CamelotException($exc);
        }
    }

    /**
     * Download a file to the client
     * @param string $fileName
     * @param bytearray $downloadedFile
     * @return void|CamelotException
     */
    private function download_file_to_client($fileName, $downloadedFile) {
        if (!empty($fileName) && strlen($downloadedFile) > 0) {
            header('Content-Description: File Transfer');
            header("Content-type: application/data");
            header('Content-Disposition: attachment; filename="' . $fileName . '"');
            header('Content-Transfer-Encoding: binary');
            header('Content-Length: ' . strlen($downloadedFile));
            ob_clean();
            flush();
            echo $downloadedFile;
        } else {
            return new CamelotException(null, 620);
        }
    }

}

/**
 * Command to download InfoPath forms
 * @author Bendsoft
 * @link http://www.bendsoft.com/documentation/camelot-net-connector/latest/procedures/downloadinfopath/ Official documentation
 */
class DownloadInfoPathCommand extends ProcedureCommandBase {

    /**
     * The name of the Document Library
     * @var string
     */
    public $listName;

    /**
     * The name the file
     * @var string
     */
    public $file;

    /**
     * Optional
     * The version number (in example 2.1)
     * @var double
     */
    public $version;

    /**
     * Optional.
     * Path where to store the file on server, if not set the file will be sent 
     * to the client (/Users/Bendsoft/Web/Temp/)
     * @var string 
     */
    public $storePath;

    /**
     * Construct a DownloadInfoPathCommand
     * @param string $connection_name
     * @param string $listName The name of the Document Library
     * @param string $file The name the file
     * @param double $version (optional) The version number (in example 2.1)
     * @param string $storePath (optional) Path where to store the file on server, if not set the file will be sent to the client (/Users/Bendsoft/Web/Temp/)
     */
    public function __construct($connection_name = null, $listName = null, $file = null, $version = null, $storePath = null) {
        $this->connection_name = $connection_name;
        $this->listName = $listName;
        $this->file = $file;
        $this->version = $version;
        $this->storePath = $storePath;
    }

    /**
     * Check validity of the command
     * @param DownloadInfoPathCommand $command
     * @return \CamelotException|boolean
     */
    public function isValid(DownloadInfoPathCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the connection name
        if (empty($command->connection_name)) {
            return new CamelotException(null, 606);
        }

        // Ojbect specific validations
        if (empty($command->listName)) {
            return new CamelotException(null, 605);
        }
        if (empty($command->file)) {
            return new CamelotException(null, 609);
        }

        // If all is OK
        return true;
    }

    /**
     * Execute the command
     * @param DownloadInfoPathCommand $command
     * @return \CamelotException|boolean
     */
    public function execute(DownloadInfoPathCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Get the connection settings from the WcfSettingsList
        $WcfSettingsList = WcfSettingsList::instance();
        $settings = $WcfSettingsList->getSettings($this->connection_name);

        if (is_null($settings)) {
            return new CamelotException(null, 800);
        }

        try {
            // Set up connection to WSDL Client
            $client = new CamelotSoapClient($settings);

            $args = array(
                'listName' => $this->listName,
                'file' => $this->file . '?NoRedirect=true',
                'version' => $this->version,
                'connectionString' => $settings->connection_string,
                'sharedKey' => $settings->wsdl_shared_key,
                'compression' => $settings->compression
            );

            // Get downloaded file from WCF Service
            $downloadedFile = $client->client->DownloadFile($args)->DownloadFileResult;

            // Send file to client or store at server
            if (empty($this->storePath)) {
                // Send to client
                header("Content-type: application/data");
                header('Content-Disposition: attachment; filename="' . pathinfo($this->file, PATHINFO_BASENAME) . '"'); // ["basename"]
                header("Content-Description: PHP Generated Data");
                echo $downloadedFile;
            } else {
                // Store on server                
                // Ensure the path ends with a /
                $storePath = rtrim($this->storePath, '/') . '/';

                // Write file
                $fp = fopen($storePath . pathinfo($this->file, PATHINFO_BASENAME), 'wb'); // ["basename"]
                fwrite($fp, $downloadedFile);
                fclose($fp);
                return true;
            }
        } catch (SoapFault $exc) {
            return new CamelotException($exc);
        }
    }

}

/**
 * Command to delete files
 * @author Bendsoft
 * @link http://www.bendsoft.com/documentation/camelot-net-connector/latest/procedures/delete/ Official documentation
 */
class DeleteCommand extends ProcedureCommandBase {

    /**
     * The name of the Document Library
     * @var string
     */
    public $listName;

    /**
     * The name of the file and path (FolderName/helloworld.txt)
     * @var string
     */
    public $file;

    /**
     * Construct a DeleteCommand
     * @param string $connection_name
     * @param string $listName The name of the Document Library
     * @param string $file The name of the file and path (FolderName/helloworld.txt)
     */
    public function __construct($connection_name = null, $listName = null, $file = null) {
        $this->connection_name = $connection_name;
        $this->listName = $listName;
        $this->file = $file;
    }

    /**
     * Check validity of the command
     * @param DeleteCommand $command
     * @return \CamelotException|boolean
     */
    public function isValid(DeleteCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the connection name
        if (empty($command->connection_name)) {
            return new CamelotException(null, 606);
        }

        // Ojbect specific validations
        if (empty($command->listName)) {
            return new CamelotException(null, 605);
        }
        if (empty($command->file)) {
            return new CamelotException(null, 609);
        }

        // If all is OK
        return true;
    }

    /**
     * Execute the command
     * @param DeleteCommand $command
     * @return \CamelotException|boolean
     */
    public function execute(DeleteCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Build the command
        $ProcedureCommand = "CALL DELETE('$command->listName', '$command->file')";

        try {
            // Build the command object
            $commandObject = new SharePointNonQueryCommand($command->connection_name, $ProcedureCommand);

            // Execute the command
            $SharePointNonQuery = new SharePointNonQuery($commandObject);

            if ($SharePointNonQuery->_result == 1) {
                return true;
            } else {
                return false;
            }
        } catch (SoapFault $exc) {
            return new CamelotException($exc);
        }
    }

}

/**
 * Command to move files
 * @author Bendsoft
 * @link http://www.bendsoft.com/documentation/camelot-net-connector/latest/procedures/move/ Official documentation
 */
class MoveCommand extends ProcedureCommandBase {

    /**
     * The name of the Source Document Library
     * @var string
     */
    public $sourceListName;

    /**
     * The name of the source file and path (FolderName/helloworld.txt)
     * @var string
     */
    public $sourceFile;

    /**
     * The name of the Destination Document Library
     * @var string
     */
    public $destinationListName;

    /**
     * The name of the destination file and path (helloworld.txt)
     * @var string
     */
    public $destinationFile;

    /**
     * Construct a MoveCommand
     * @param string $connection_name
     * @param string $sourceListName The name of the Source Document Library
     * @param string $sourceFile The name of the source file and path (FolderName/helloworld.txt)
     * @param string $destinationListName The name of the Destination Document Library
     * @param string $destinationFile The name of the destination file and path (helloworld.txt)
     */
    public function __construct($connection_name = null, $sourceListName = null, $sourceFile = null, $destinationListName = null, $destinationFile = null) {
        $this->connection_name = $connection_name;
        $this->sourceListName = $sourceListName;
        $this->sourceFile = $sourceFile;
        $this->destinationListName = $destinationListName;
        $this->destinationFile = $destinationFile;
    }

    /**
     * Check validity of the command
     * @param MoveCommand $command
     * @return \CamelotException|boolean
     */
    public function isValid(MoveCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the connection name
        if (empty($command->connection_name)) {
            return new CamelotException(null, 606);
        }

        // Ojbect specific validations
        if (empty($command->sourceListName)) {
            return new CamelotException(null, 610);
        }

        if (empty($command->sourceFile)) {
            return new CamelotException(null, 611);
        }

        if (empty($command->destinationListName)) {
            return new CamelotException(null, 612);
        }

        if (empty($command->destinationFile)) {
            return new CamelotException(null, 613);
        }

        // If all is OK
        return true;
    }

    /**
     * Execute the command
     * @param MoveCommand $command
     * @return \CamelotException|boolean
     */
    public function execute(MoveCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Build the command
        $ProcedureCommand = "CALL MOVE('$command->sourceListName', '$command->sourceFile', '$command->destinationListName', '$command->destinationFile')";

        try {
            // Build the command object
            $commandObject = new SharePointNonQueryCommand($command->connection_name, $ProcedureCommand);

            // Execute the command
            $SharePointNonQuery = new SharePointNonQuery($commandObject);

            if ($SharePointNonQuery->_result == 1) {
                return true;
            } else {
                return false;
            }
        } catch (SoapFault $exc) {
            return new CamelotException($exc);
        }
    }

}

/**
 * Command to copy files
 * @author Bendsoft
 * @link http://www.bendsoft.com/documentation/camelot-net-connector/latest/procedures/copy/ Official documentation
 */
class CopyCommand extends ProcedureCommandBase {

    /**
     * The name of the Source Document Library
     * @var string
     */
    public $sourceListName;

    /**
     * The name of the source file and path (FolderName/helloworld.txt)
     * @var string
     */
    public $sourceFile;

    /**
     * The name of the Destination Document Library
     * @var string
     */
    public $destinationListName;

    /**
     * The name of the destination file and path (helloworld.txt)
     * @var string
     */
    public $destinationFile;

    /**
     * Construct a CopyCommand
     * @param string $connection_name
     * @param string $sourceListName The name of the Source Document Library
     * @param string $sourceFile The name of the source file and path (FolderName/helloworld.txt)
     * @param string $destinationListName The name of the Destination Document Library
     * @param string $destinationFile The name of the destination file and path (helloworld.txt)
     */
    public function __construct($connection_name = null, $sourceListName = null, $sourceFile = null, $destinationListName = null, $destinationFile = null) {
        $this->connection_name = $connection_name;
        $this->sourceListName = $sourceListName;
        $this->sourceFile = $sourceFile;
        $this->destinationListName = $destinationListName;
        $this->destinationFile = $destinationFile;
    }

    /**
     * Check validity of the command
     * @param CopyCommand $command
     * @return \CamelotException|boolean
     */
    public function isValid(CopyCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the connection name
        if (empty($command->connection_name)) {
            return new CamelotException(null, 606);
        }

        // Ojbect specific validations
        if (empty($command->sourceListName)) {
            return new CamelotException(null, 610);
        }
        if (empty($command->sourceFile)) {
            return new CamelotException(null, 611);
        }
        if (empty($command->destinationListName)) {
            return new CamelotException(null, 612);
        }
        if (empty($command->destinationFile)) {
            return new CamelotException(null, 613);
        }

        // If all is OK
        return true;
    }

    /**
     * Execute the command
     * @param CopyCommand $command
     * @return \CamelotException|boolean
     */
    public function execute(CopyCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Build the command
        $ProcedureCommand = "CALL COPY('$command->sourceListName', '$command->sourceFile', '$command->destinationListName', '$command->destinationFile')";

        try {
            // Build the command object
            $commandObject = new SharePointNonQueryCommand($command->connection_name, $ProcedureCommand);

            // Execute the command
            $SharePointNonQuery = new SharePointNonQuery($commandObject);

            if ($SharePointNonQuery->_result == 1) {
                return true;
            } else {
                return false;
            }
        } catch (SoapFault $exc) {
            return new CamelotException($exc);
        }
    }

}

/**
 * Command to rename files
 * @author Bendsoft
 * @link http://www.bendsoft.com/documentation/camelot-net-connector/latest/procedures/rename/ Official documentation
 */
class RenameCommand extends ProcedureCommandBase {

    /**
     * The name of the Document Library
     * @var string
     */
    public $listName;

    /**
     * The name of the source file and path (FolderName/helloworld.txt)
     * @var string
     */
    public $sourceFile;

    /**
     * The name of the destination file and path (FolderName/helloworld_renamed.txt)
     * @var string
     */
    public $destinationFile;

    /**
     * Construct a RenameCommand
     * @param string $connection_name
     * @param string $listName The name of the Document Library
     * @param string $sourceFile The name of the source file and path (FolderName/helloworld.txt)
     * @param string $destinationFile name of the destination file and path (FolderName/helloworld_renamed.txt)
     */
    public function __construct($connection_name = null, $listName = null, $sourceFile = null, $destinationFile = null) {
        $this->connection_name = $connection_name;
        $this->listName = $listName;
        $this->sourceFile = $sourceFile;
        $this->destinationFile = $destinationFile;
    }

    /**
     * Check validity of the command
     * @param RenameCommand $command
     * @return \CamelotException|boolean
     */
    public function isValid(RenameCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the connection name
        if (empty($command->connection_name)) {
            return new CamelotException(null, 606);
        }

        // Ojbect specific validations
        if (empty($command->listName)) {
            return new CamelotException(null, 605);
        }
        if (empty($command->sourceFile)) {
            return new CamelotException(null, 611);
        }
        if (empty($command->destinationFile)) {
            return new CamelotException(null, 613);
        }

        // If all is OK
        return true;
    }

    /**
     * Execute the command
     * @param RenameCommand $command
     * @return \CamelotException|boolean
     */
    public function execute(RenameCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Build the command
        $ProcedureCommand = "CALL RENAME('$command->listName', '$command->sourceFile', '$command->destinationFile')";

        try {
            // Build the command object
            $commandObject = new SharePointNonQueryCommand($command->connection_name, $ProcedureCommand);

            // Execute the command
            $SharePointNonQuery = new SharePointNonQuery($commandObject);

            if ($SharePointNonQuery->_result == 1) {
                return true;
            } else {
                return false;
            }
        } catch (SoapFault $exc) {
            return new CamelotException($exc);
        }
    }

}

/**
 * Command to checkout a file
 * @author Bendsoft
 * @link http://www.bendsoft.com/documentation/camelot-net-connector/latest/procedures/checkout/ Official documentation
 */
class CheckoutCommand extends ProcedureCommandBase {

    /**
     * The name of the Document Library
     * @var string
     */
    public $listName;

    /**
     * The name of the file and path (FolderName/helloworld.txt)
     * @var string
     */
    public $file;

    /**
     * Optional
     * Indicates whether the file is to be flagged as checked out for offline editing.
     * @var string
     */
    public $offlineEditing;

    /**
     * Optional
     * Representing the date and time of the last modification of the file. If this 
     * value is provided, the server compares the submitted value with the local 
     * last modified date. If the values do not match, the check-out fails and this 
     * procedure returns false
     * @var datetime
     */
    public $lastModified;

    /**
     * Construct a CheckoutCommand
     * @param string $connection_name
     * @param string $listName The name of the Document Library
     * @param string $file The name of the file and path (FolderName/helloworld.txt)
     * @param bool $offlineEditing (optional) Indicates whether the file is to be flagged as checked out for offline editing.
     * @param datetime $lastModified (optional) Representing the date and time of the last modification of the file
     */
    public function __construct($connection_name = null, $listName = null, $file = null, $offlineEditing = true, $lastModified = null) {
        $this->connection_name = $connection_name;
        $this->listName = $listName;
        $this->file = $file;
        $this->offlineEditing = $offlineEditing;
        $this->lastModified = $lastModified;
    }

    /**
     * Check validity of the command
     * @param CheckoutCommand $command
     * @return \CamelotException|boolean
     */
    public function isValid(CheckoutCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the connection name
        if (empty($command->connection_name)) {
            return new CamelotException(null, 606);
        }

        // Ojbect specific validations
        if (empty($command->listName)) {
            return new CamelotException(null, 605);
        }
        if (empty($command->file)) {
            return new CamelotException(null, 609);
        }

        // If all is OK
        return true;
    }

    /**
     * Execute the command
     * @param CheckoutCommand $command
     * @return \CamelotException|boolean
     */
    public function execute(CheckoutCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Set offlineEditing
        $offlineEditing = ($command->offlineEditing) ? "true" : "false";

        // Set lastModified
        $lastModified = empty($this->lastModified) ? "" : ", '$this->lastModified'";

        // Build the command
        $ProcedureCommand = "CALL CHECKOUT('$command->listName', '$command->file', $offlineEditing$lastModified)";

        try {
            // Build the command object
            $commandObject = new SharePointNonQueryCommand($command->connection_name, $ProcedureCommand);

            // Execute the command
            $SharePointNonQuery = new SharePointNonQuery($commandObject);

            if ($SharePointNonQuery->_result == 1) {
                return true;
            } else {
                return false;
            }
        } catch (SoapFault $exc) {
            return new CamelotException($exc);
        }
    }

}

/**
 * Command to delete files
 * @author Bendsoft
 * @link http://www.bendsoft.com/documentation/camelot-net-connector/latest/procedures/checkin/ Official documentation
 */
class CheckinCommand extends ProcedureCommandBase {

    /**
     * The name of the Document Library
     * @var string
     */
    public $listName;

    /**
     * The name of the file and path (FolderName/helloworld.txt)
     * @var string
     */
    public $file;

    /**
     * Check-in comments
     * @var string
     */
    public $comment;

    /**
     * indicating the check-in method used. Valid values are: 0 = MinorCheckIn, 
     * 1 = MajorCheckIn, and 2 = OverwriteCheckIn.
     * @var int
     */
    public $checkInType;

    /**
     * Construct a CheckinCommand
     * @param string $connection_name
     * @param string $listName The name of the Document Library
     * @param string $file The name of the file and path (FolderName/helloworld.txt)
     * @param string $comment Check-in comments
     * @param int $checkInType indicating the check-in method used. Valid values are: 0 = MinorCheckIn, 1 = MajorCheckIn, and 2 = OverwriteCheckIn.
     */
    public function __construct($connection_name = null, $listName = null, $file = null, $comment = null, $checkInType = null) {
        $this->connection_name = $connection_name;
        $this->listName = $listName;
        $this->file = $file;
        $this->comment = $comment;
        $this->checkInType = $checkInType;
    }

    /**
     * Check validity of the command
     * @param CheckinCommand $command
     * @return \CamelotException|boolean
     */
    public function isValid(CheckinCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the connection name
        if (empty($command->connection_name)) {
            return new CamelotException(null, 606);
        }

        // Ojbect specific validations
        if (empty($command->listName)) {
            return new CamelotException(null, 605);
        }
        if (empty($command->file)) {
            return new CamelotException(null, 609);
        }
        if (empty($command->checkInType)) {
            return new CamelotException(null, 614);
        }

        // If all is OK
        return true;
    }

    /**
     * Execute the command
     * @param CheckinCommand $command
     * @return \CamelotException|boolean
     */
    public function execute(CheckinCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Build the command
        $ProcedureCommand = "CALL CHECKIN('$command->listName', '$command->file', '$command->comment', $command->checkInType)";

        try {
            // Build the command object
            $commandObject = new SharePointNonQueryCommand($command->connection_name, $ProcedureCommand);

            // Execute the command
            $SharePointNonQuery = new SharePointNonQuery($commandObject);

            if ($SharePointNonQuery->_result == 1) {
                return true;
            } else {
                return false;
            }
        } catch (SoapFault $exc) {
            return new CamelotException($exc);
        }
    }

}

/**
 * Command to undo a checkout
 * @author Bendsoft
 * @link http://www.bendsoft.com/documentation/camelot-net-connector/latest/procedures/undocheckout/ Official documentation
 */
class UndoCheckoutCommand extends ProcedureCommandBase {

    /**
     * The name of the Document Library
     * @var string
     */
    public $listName;

    /**
     * The name of the file and path (FolderName/helloworld.txt)
     * @var string
     */
    public $file;

    /**
     * Construct an UndoCheckoutCommand
     * @param string $connection_name
     * @param string $listName
     * @param string $file
     */
    public function __construct($connection_name = null, $listName = null, $file = null) {
        $this->connection_name = $connection_name;
        $this->listName = $listName;
        $this->file = $file;
    }

    /**
     * Check validity of the command
     * @param UndoCheckoutCommand $command
     * @return \CamelotException|boolean
     */
    public function isValid(UndoCheckoutCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the connection name
        if (empty($command->connection_name)) {
            return new CamelotException(null, 606);
        }

        // Ojbect specific validations
        if (empty($command->listName)) {
            return new CamelotException(null, 605);
        }
        if (empty($command->file)) {
            return new CamelotException(null, 609);
        }

        // If all is OK
        return true;
    }

    /**
     * Execute the command
     * @param UndoCheckoutCommand $command
     * @return \CamelotException|boolean
     */
    public function execute(UndoCheckoutCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Build the command
        $ProcedureCommand = "CALL UNDOCHECKOUT('$command->listName', '$command->file')";

        try {
            // Build the command object
            $commandObject = new SharePointNonQueryCommand($command->connection_name, $ProcedureCommand);

            // Execute the command
            $SharePointNonQuery = new SharePointNonQuery($commandObject);

            if ($SharePointNonQuery->_result == 1) {
                return true;
            } else {
                return false;
            }
        } catch (SoapFault $exc) {
            return new CamelotException($exc);
        }
    }

}

/**
 * Command to get all versions of a specific document
 * @author Bendsoft
 * @link http://www.bendsoft.com/documentation/camelot-net-connector/latest/procedures/getversions/ Official documentation
 */
class GetVersionsCommand extends ProcedureCommandBase {

    /**
     * The name of the Document Library
     * @var string
     */
    public $listName;

    /**
     * The name of the file and path (FolderName/helloworld.txt)
     * @var string
     */
    public $file;

    /**
     * Construct a GetVersionsCommand
     * @param string $connection_name 
     * @param string $listName The name of the Document Library
     * @param string $file The name of the file and path (FolderName/helloworld.txt)
     */
    public function __construct($connection_name = null, $listName = null, $file = null) {
        $this->connection_name = $connection_name;
        $this->listName = $listName;
        $this->file = $file;
    }

    /**
     * Check validity of the command
     * @param GetVersionsCommand $command
     * @return \CamelotException|boolean
     */
    public function isValid(GetVersionsCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the connection name
        if (empty($command->connection_name)) {
            return new CamelotException(null, 606);
        }

        // Ojbect specific validations
        if (empty($command->listName)) {
            return new CamelotException(null, 605);
        }
        if (empty($command->file)) {
            return new CamelotException(null, 609);
        }

        // If all is OK
        return true;
    }

    /**
     * Execute the command
     * @param GetVersionsCommand $command
     * @return \CamelotException|\SharePointQuery
     */
    public function execute(GetVersionsCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Build the command
        $ProcedureCommand = "CALL GETVERSIONS('$command->listName', '$command->file')";

        try {
            // Build the command object
            $commandObject = new SharePointSqlCommand($command->connection_name, $ProcedureCommand);

            // Execute the command
            $SharePointQuery = new SharePointQuery($commandObject);

            return $SharePointQuery;
        } catch (SoapFault $exc) {
            return new CamelotException($exc);
        }
    }

}

/**
 * Command to get all versions of specified columns of a specific list item
 * @author Bendsoft
 * @since 2.1
 * @link http://www.bendsoft.com/documentation/camelot-net-connector/latest/procedures/getcolumnversions/ Official documentation
 */
class GetColumnVersionsCommand extends ProcedureCommandBase {

    /**
     * The name of the Document Library
     * @var string
     */
    public $listName;

    /**
     * The ID of the list item
     * @var int
     */
    public $itemId;

    /**
     * Comma separated list with the columns to retrieve
     * @var string
     */
    public $columnNames;

    /**
     * Construct a GetColumnVersionsCommand
     * @param string $connection_name 
     * @param string $listName The name of the Document Library
     * @param string $itemId The ID of the list item
     * @param string $columnNames Comma separated list with the columns to retrieve
     */
    public function __construct($connection_name = null, $listName = null, $itemId = null, $columnNames = null) {
        $this->connection_name = $connection_name;
        $this->listName = $listName;
        $this->itemId = $itemId;
        $this->columnNames = $columnNames;
    }

    /**
     * Check validity of the command
     * @param GetColumnVersionsCommand $command
     * @return \CamelotException|boolean
     */
    public function isValid(GetColumnVersionsCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the connection name
        if (empty($command->connection_name)) {
            return new CamelotException(null, 606);
        }

        // Ojbect specific validations
        if (empty($command->listName)) {
            return new CamelotException(null, 605);
        }
        if (empty($command->itemId)) {
            return new CamelotException(null, 616);
        }
        if (empty($command->columnNames)) {
            return new CamelotException(null, 621);
        }

        // If all is OK
        return true;
    }

    /**
     * Execute the command
     * @param GetColumnVersionsCommand $command
     * @return \CamelotException|\SharePointQuery
     */
    public function execute(GetColumnVersionsCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Build the command
        $ProcedureCommand = "CALL GETCOLUMNVERSIONS('$command->listName', '$command->itemId', '$command->columnNames')";

        try {
            // Build the command object
            $commandObject = new SharePointSqlCommand($command->connection_name, $ProcedureCommand);

            // Execute the command
            $SharePointQuery = new SharePointQuery($commandObject);

            return $SharePointQuery;
        } catch (SoapFault $exc) {
            return new CamelotException($exc);
        }
    }

}

/**
 * Command to delete a version of a specific document
 * @author Bendsoft
 * @link http://www.bendsoft.com/documentation/camelot-net-connector/latest/procedures/deleteversion/ Official documentation
 */
class DeleteVersionCommand extends ProcedureCommandBase {

    /**
     * The name of the Document Library
     * @var string
     */
    public $listName;

    /**
     * The name of the file and path (FolderName/helloworld.txt)
     * @var string
     */
    public $file;

    /**
     * The version number (in example 2.1)
     * @var double
     */
    public $version;

    /**
     * Construct a DeleteVersionCommand
     * @param string $connection_name
     * @param string $listName The name of the Document Library
     * @param string $file The name of the file and path (FolderName/helloworld.txt)
     * @param double $version The version number (in example 2.1)
     */
    public function __construct($connection_name = null, $listName = null, $file = null, $version = null) {
        $this->connection_name = $connection_name;
        $this->listName = $listName;
        $this->file = $file;
        $this->version = $version;
    }

    /**
     * Check validity of the command
     * @param DeleteVersionCommand $command
     * @return \CamelotException|boolean
     */
    public function isValid(DeleteVersionCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the connection name
        if (empty($command->connection_name)) {
            return new CamelotException(null, 606);
        }

        // Ojbect specific validations
        if (empty($command->listName)) {
            return new CamelotException(null, 605);
        }
        if (empty($command->file)) {
            return new CamelotException(null, 609);
        }
        if (empty($command->version)) {
            return new CamelotException(null, 615);
        }

        // If all is OK
        return true;
    }

    /**
     * Execute the command
     * @param DeleteVersionCommand $command
     * @return \CamelotException|boolean
     */
    public function execute(DeleteVersionCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Build the command
        $ProcedureCommand = "CALL DELETEVERSION('$command->listName', '$command->file', $command->version)";

        try {
            // Build the command object
            $commandObject = new SharePointSqlCommand($command->connection_name, $ProcedureCommand);

            // Execute the command
            $SharePointQuery = new SharePointQuery($commandObject);

            return $SharePointQuery;
        } catch (SoapFault $exc) {
            return new CamelotException($exc);
        }
    }

}

/**
 * Command to delete all versions of a specific document except the current version
 * @author Bendsoft
 * @link http://www.bendsoft.com/documentation/camelot-net-connector/latest/procedures/deleteallversions/ Official documentation
 */
class DeleteAllVersionsCommand extends ProcedureCommandBase {

    /**
     * The name of the Document Library
     * @var string
     */
    public $listName;

    /**
     * The name of the file and path (FolderName/helloworld.txt)
     * @var string
     */
    public $file;

    /**
     * Construct a DeleteAllVersionsCommand
     * @param string $connection_name
     * @param string $listName The name of the Document Library
     * @param string $file The name of the file and path (FolderName/helloworld.txt)
     */
    public function __construct($connection_name = null, $listName = null, $file = null) {
        $this->connection_name = $connection_name;
        $this->listName = $listName;
        $this->file = $file;
    }

    /**
     * Check validity of the command
     * @param DeleteAllVersionsCommand $command
     * @return \CamelotException|boolean
     */
    public function isValid(DeleteAllVersionsCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the connection name
        if (empty($command->connection_name)) {
            return new CamelotException(null, 606);
        }

        // Ojbect specific validations
        if (empty($command->listName)) {
            return new CamelotException(null, 605);
        }
        if (empty($command->file)) {
            return new CamelotException(null, 609);
        }

        // If all is OK
        return true;
    }

    /**
     * Execute the command
     * @param DeleteAllVersionsCommand $command
     * @return \CamelotException|boolean
     */
    public function execute(DeleteAllVersionsCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Build the command
        $ProcedureCommand = "CALL DELETEALLVERSIONS('$command->listName', '$command->file')";

        try {
            // Build the command
            $commandObject = new SharePointSqlCommand($command->connection_name, $ProcedureCommand);

            // Execute the command
            $SharePointQuery = new SharePointQuery($commandObject);

            return $SharePointQuery;
        } catch (SoapFault $exc) {
            return new CamelotException($exc);
        }
    }

}

/**
 * Command to restore a version of a specific document. This will replace the current version of the document with the restored version.
 * @author Bendsoft
 * @link http://www.bendsoft.com/documentation/camelot-net-connector/latest/procedures/restoreversion/ Official documentation
 */
class RestoreVersionCommand extends ProcedureCommandBase {

    /**
     * The name of the Document Library
     * @var string
     */
    public $listName;

    /**
     * The name of the file and path (FolderName/helloworld.txt)
     * @var string
     */
    public $file;

    /**
     * The version number (in example 2.1)
     * @var double
     */
    public $version;

    /**
     * Construct a RestoreVersionCommand
     * @param string $connection_name
     * @param string $listName The name of the Document Library
     * @param string $file The name of the file and path (FolderName/helloworld.txt)
     * @param double $version The version number (in example 2.1)
     */
    public function __construct($connection_name = null, $listName = null, $file = null, $version = null) {
        $this->connection_name = $connection_name;
        $this->listName = $listName;
        $this->file = $file;
        $this->version = $version;
    }

    /**
     * Check validity of the command
     * @param RestoreVersionCommand $command
     * @return \CamelotException|boolean
     */
    public function isValid(RestoreVersionCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the connection name
        if (empty($command->connection_name)) {
            return new CamelotException(null, 606);
        }

        // Ojbect specific validations
        if (empty($command->listName)) {
            return new CamelotException(null, 605);
        }
        if (empty($command->file)) {
            return new CamelotException(null, 609);
        }
        if (empty($command->version)) {
            return new CamelotException(null, 615);
        }

        // If all is OK
        return true;
    }

    /**
     * Execute the command
     * @param RestoreVersionCommand $command
     * @return \CamelotException|boolean
     */
    public function execute(RestoreVersionCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Build the command
        $ProcedureCommand = "CALL RESTOREVERSION('$command->listName', '$command->file', $command->version)";

        try {
            // Build the command
            $commandObject = new SharePointSqlCommand($command->connection_name, $ProcedureCommand);

            // Execute the command
            $SharePointQuery = new SharePointQuery($commandObject);

            return $SharePointQuery;
        } catch (SoapFault $exc) {
            return new CamelotException($exc);
        }
    }

}

/**
 * Command download one or several attachements
 * @author Bendsoft
 * @link http://www.bendsoft.com/documentation/camelot-net-connector/latest/procedures/downloadattachment/ Official documentation
 */
class DownloadAttachmentCommand extends ProcedureCommandBase {

    /**
     * The name of the Document Library
     * @var string
     */
    public $listName;

    /**
     * List Item ID
     * @var int
     */
    public $itemId;

    /**
     * Optional
     * Indicates if column Data is included in the results, default to true
     * @var bool
     */
    public $includeData;

    /**
     * Optional
     * File name of attachment to download, if not stated all files are downloaded
     * @var string
     */
    public $fileName;

    /**
     * Optional.
     * Path where to store the file on server, if not set the file will be sent 
     * to the client browser
     * @var string 
     */
    public $storePath;

    /**
     * Construct a DownloadAttachmentCommand
     * @param string $connection_name
     * @param string $listName The name of the Document Library
     * @param int $itemId List Item ID
     * @param string $fileName (optional) File name of attachment to download, if not stated all files are downloaded
     * @param type $storePath (optional) Where to store the file on the server, if not set the file will be sent to the client browser
     * @param bool $includeData (optional) Indicates if column Data is included in the results, default to true
     */
    public function __construct($connection_name = null, $listName = null, $itemId = null, $fileName = null, $storePath = null, $includeData = true) {
        $this->connection_name = $connection_name;
        $this->listName = $listName;
        $this->itemId = $itemId;
        $this->includeData = $includeData;
        $this->fileName = $fileName;
        $this->storePath = $storePath;
    }

    /**
     * Check validity of the command
     * @param DownloadAttachmentCommand $command
     * @return \CamelotException|boolean
     */
    public function isValid(DownloadAttachmentCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the connection name
        if (empty($command->connection_name)) {
            return new CamelotException(null, 606);
        }

        // Ojbect specific validations
        if (empty($command->listName)) {
            return new CamelotException(null, 605);
        }
        if (empty($command->itemId)) {
            return new CamelotException(null, 616);
        }

        // If all is OK
        return true;
    }

    /**
     * Execute the command
     * @param DownloadAttachmentCommand $command
     * @return \CamelotException|boolean
     */
    public function execute(DownloadAttachmentCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        $includeData = ($command->includeData) ? "true" : "false";
        $fileName = empty($command->fileName) ? "" : ", '$command->fileName'";

        // Build the command
        $ProcedureCommand = "CALL DOWNLOADATTACHMENT('$command->listName', $command->itemId, $includeData$fileName)";

        try {
            // Build the command object
            $commandObject = new SharePointSqlCommand($command->connection_name, $ProcedureCommand);

            // Execute the command
            $SharePointQuery = new SharePointQuery($commandObject);

            // Count number of attachements
            $count = count($SharePointQuery->CamelotSoap->_sorted->_content);

            // Return false if there are no attachements
            if ($count == 0) {
                return false;
            }

            // start zip archive
            $zip = null;
            $zipFile = null;

            foreach ($SharePointQuery->CamelotSoap->_sorted->_content as $value) {
                $fileName = $value["FileName"];
                $data = $value["Data"];
                $itemId = $value["ItemID"];

                // Check if there is any data
                if (empty($data)) {
                    continue;
                }

                if (!empty($this->storePath)) {
                    // If the files should be stored at the server
                    // Ensure the path ends with a /
                    $storePath = rtrim($this->storePath, '/') . '/';

                    // Write file
                    $fp = fopen($storePath . $fileName, 'wb');
                    fwrite($fp, base64_decode($data));
                    fclose($fp);
                } else {
                    // Store in temp dir
                    $storePath = rtrim(TEMP_DIR, '/') . '/';

                    $fp = fopen($storePath . $fileName, 'wb');
                    fwrite($fp, base64_decode($data));
                    fclose($fp);

                    if ($count == 1) {

                        // Download to client
                        $this->download_file_to_client($storePath . $fileName);

                        // delete temp file
                        unlink($storePath . $fileName);
                    } else {
                        // Add to zip archive
                        if (is_null($zip)) {
                            $zip = new ZipArchive();
                        }

                        $zipFile = "$storePath$itemId.zip";
                        if ($zip->open($zipFile, ZIPARCHIVE::CREATE) === TRUE) {
                            $zip->addFile($storePath . $fileName, $fileName);
                            $zip->close();

                            // delete temp file
                            unlink($storePath . $fileName);
                        }
                    }
                }
            }

            // If we added files to our zip archive send it to the browser
            if (!is_null($zip) && !is_null($zipFile)) {

                // Download to client
                $downloadResult = $this->download_file_to_client($zipFile);

                // delete temp file
                unlink($zipFile);

                // If the $downloadResult is set we have an exception
                if (isset($downloadResult)) {
                    return $downloadResult;
                }
            }

            return $SharePointQuery;
        } catch (SoapFault $exc) {
            return new CamelotException($exc);
        }
    }

    /**
     * Download a file to the client
     * @param string $filePath
     * @return void|CamelotException
     */
    private function download_file_to_client($filePath) {
        if (file_exists($filePath)) {
            header('Content-Description: File Transfer');
            header("Content-type: application/data");
            header('Content-Disposition: attachment; filename="' . pathinfo($filePath, PATHINFO_BASENAME) . '"'); // ["basename"]
            header('Content-Transfer-Encoding: binary');
            header('Content-Length: ' . filesize($filePath));
            ob_clean();
            flush();
            readfile($filePath);
        } else {
            return new CamelotException(null, 620);
        }
    }

}

/**
 * Command to upload an attachement
 * @author Bendsoft
 * @link http://www.bendsoft.com/documentation/camelot-net-connector/latest/procedures/uploadattachment/ Official documentation
 */
class UploadAttachmentCommand extends ProcedureCommandBase {

    /**
     * The name of the Document Library
     * @var string
     */
    public $listName;

    /**
     * List Item ID
     * @var int
     */
    public $itemId;

    /**
     * (optional) File name of attachment to upload
     * @var string
     */
    public $fileName;

    /**
     * The post data, the byte array or the $_FILES object
     * @var bytearray|array
     */
    public $data;

    /**
     * Optional.
     * Path to file if uploading directly from disk (c:\temp\file.docx)
     * @var string
     */
    public $filePath;

    /**
     * Private store for the bytearray
     * @var bytearray
     */
    private $byteArray;

    /**
     * Construct a UploadAttachmentCommand
     * @param string $connection_name
     * @param string $listName The name of the Document Library
     * @param int $itemId List Item ID
     * @param string (optional) $fileName File name of attachment to upload
     * @param bytearray|array $data The post data, the byte array or the $_FILES object
     * @param string $filePath (optional) Path to file if uploading directly from disk (c:\temp\file.docx)
     */
    public function __construct($connection_name = null, $listName = null, $itemId = null, $fileName = null, $data = null, $filePath = null) {
        $this->connection_name = $connection_name;
        $this->listName = $listName;
        $this->itemId = $itemId;
        $this->fileName = $fileName;
        $this->data = $data;
        $this->filePath = $filePath;
    }

    /**
     * Check validity of the command
     * @param UploadAttachmentCommand $command
     * @return \CamelotException|boolean
     */
    public function isValid(UploadAttachmentCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the connection name
        if (empty($command->connection_name)) {
            return new CamelotException(null, 606);
        }

        // Ojbect specific validations
        if (empty($command->listName)) {
            return new CamelotException(null, 605);
        }
        if (empty($command->itemId)) {
            return new CamelotException(null, 616);
        }
        //if (empty($command->fileName)) {
        //    return new CamelotException(null, 617);
        //}
        //if (empty($command->data)) {
        //    return new CamelotException(null, 618);
        //}
        // If all is OK
        return true;
    }

    /**
     * Execute the command
     * @param UploadAttachmentCommand $command
     * @return CamelotException|boolean
     */
    public function execute(UploadAttachmentCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the file
        $validateFileResult = $this->validate_file();
        if ($validateFileResult !== true && $validateFileResult instanceof CamelotException) {
            // Return the exception if it fails
            return $validateFileResult;
        }

        $uploadFileResult = $this->upload_file();
        return $uploadFileResult;
    }

    /**
     * Method to validate the file
     * @return CamelotException|boolean
     */
    private function validate_file() {

        $file = "";
        $fileName = "";
        $size = 0;

        // If we are handing a $_FILES object
        if (gettype($this->data) == "array" && isset($this->data['file']) && $this->data['file']['error'] != 4) {

            $file = $this->data["file"];
            $fileName = $file["name"];
            $size = $file["size"];

            // Set the filename
            $this->fileName = empty($this->fileName) ? $fileName : $this->fileName;

            // Check for errors
            if ($file["error"] > 0) {
                return new CamelotException(null, 604);
            }

            // Set the byte array
            $this->byteArray = file_get_contents($file['tmp_name']);
        } elseif (!empty($this->filePath)) {
            $fileName = pathinfo($this->filePath, PATHINFO_BASENAME); // ["basename"]
            $size = filesize($this->filePath);

            // Set the filename
            $this->fileName = empty($this->fileName) ? $fileName : $this->fileName;

            // Set the byte array
            $this->byteArray = file_get_contents($this->filePath);
        } elseif (isset($this->data) && gettype($this->data) != "array") {
            // If we are here a bytearray should have been loaded in the data property
            // Get the size
            $size = strlen($this->data);

            // if the filename isn't set return error
            if (empty($this->fileName)) {
                return new CamelotException(null, 619);
            }

            // Set the filename
            $fileName = empty($this->fileName) ? $fileName : $this->fileName;

            // Set the byte array
            $this->byteArray = $data;
        } else {
            // If nothing is set
            return new CamelotException(null, 600);
        }


        // Check valid file types
        if (constant("FILE_TYPES") != "*") {
            // Get file extension
            $ext = pathinfo($fileName, PATHINFO_EXTENSION);

            // Turn valid file types into an array
            $valid_extensions = explode(",", constant("FILE_TYPES"));

            // If not found...
            if (!in_array(strtolower($ext), array_map('strtolower', $valid_extensions))) {
                return new CamelotException(null, 602);
            }
        }

        // File size checks
        // ...check the file is larger than zero
        if (!$size > 0) {
            return new CamelotException(null, 603);
        }

        // ...check max size
        if (constant("FILE_SIZE") != "*") {
            // If bigger than the allowed maximum
            if ($size > (int) constant("FILE_SIZE")) {
                return new CamelotException(null, 601);
            }
        }

        // Add more validation methods if required...
        return true;
    }

    /**
     * Perform the file upload
     * @return \CamelotException|boolean
     */
    private function upload_file() {

        // Get the connection settings from the WcfSettingsList
        $WcfSettingsList = WcfSettingsList::instance();
        $settings = $WcfSettingsList->getSettings($this->connection_name);

        if (is_null($settings)) {
            return new CamelotException(null, 800);
        }

        try {
            // Set up connection to WSDL Client
            $client = new CamelotSoapClient($settings);

            $folder = (isset($this->folder)) ? rtrim($this->folder, '/') . '/' : "";

            $args = array(
                'listName' => $this->listName,
                'itemId' => $this->itemId,
                'fileName' => $folder . $this->fileName,
                'data' => $this->byteArray,
                'connectionString' => $settings->connection_string,
                'sharedKey' => $settings->wsdl_shared_key
            );

            // Send file to WCF service and store result
            $uploadFile = $client->client->UploadAttachment($args)->UploadAttachmentResult;

            if ($uploadFile == 1) {
                // If success
                return true;
            } else {
                // If fail
                return false;
            }
        } catch (SoapFault $exc) {
            // if we had a script error
            return new CamelotException($exc);
        }
    }

    /**
     * Destroy
     */
    public function __destruct() {
        unset($this->byteArray);
        unset($this->data);
    }

}

/**
 * Command to delete an attachement
 * @author Bendsoft
 * @link http://www.bendsoft.com/documentation/camelot-net-connector/latest/procedures/deleteattachment/ Official documentation
 */
class DeleteAttachmentCommand extends ProcedureCommandBase {

    /**
     * The name of the Document Library
     * @var string
     */
    public $listName;

    /**
     * List Item ID
     * @var int
     */
    public $itemId;

    /**
     * File name of attachment to delete
     * @var string
     */
    public $fileName;

    /**
     * Construct a DeleteAttachmentCommand
     * @param string $connection_name
     * @param string $listName The name of the Document Library
     * @param int $itemId List Item ID
     * @param string $fileName File name of attachment to delete
     */
    public function __construct($connection_name = null, $listName = null, $itemId = null, $fileName = null) {
        $this->connection_name = $connection_name;
        $this->listName = $listName;
        $this->itemId = $itemId;
        $this->fileName = $fileName;
    }

    /**
     * Check validity of the command
     * @param DeleteAttachmentCommand $command
     * @return \CamelotException|boolean
     */
    public function isValid(DeleteAttachmentCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Validate the connection name
        if (empty($command->connection_name)) {
            return new CamelotException(null, 606);
        }

        // Ojbect specific validations
        if (empty($command->listName)) {
            return new CamelotException(null, 605);
        }
        if (empty($command->itemId)) {
            return new CamelotException(null, 616);
        }
        if (empty($command->fileName)) {
            return new CamelotException(null, 617);
        }

        // If all is OK
        return true;
    }

    /**
     * Execute the command
     * @param DeleteAttachmentCommand $command
     * @return \CamelotException|boolean
     */
    public function execute(DeleteAttachmentCommand $command = null) {
        // Set $command as the current instace if $command is null and the instance is set
        if ($command == null && isset($this)) {
            $command = $this;
        }

        // Build the command
        $ProcedureCommand = "CALL DELETEATTACHMENT('$command->listName', $command->itemId, '$command->fileName')";

        try {
            // Build the command object
            $commandObject = new SharePointNonQueryCommand($command->connection_name, $ProcedureCommand);

            // Execute the command
            $SharePointNonQuery = new SharePointNonQuery($commandObject);

            if ($SharePointNonQuery->_result == 1) {
                return true;
            } else {
                return false;
            }
        } catch (SoapFault $exc) {
            return new CamelotException($exc);
        }
    }

}

?>
